
#include "main.h"
#include "inc/hw_udma.h"
#include "inc/hw_uart.h"

#include "utils/uartstdio.h"

#include "driverlib/uart.h"
#include "driverlib/udma.h"

#if defined(__TI_COMPILER_VERSION__)
#pragma DATA_ALIGN(dmaControlTable, 1024)
#elif defined(__IAR_SYSTEMS_ICC__)
#pragma data_alignment = 1024
#elif defined(__GNUC__)
__attribute__((aligned(1024)))
#endif
static tDMAControlTable dmaControlTable[15+32]; // only dmaControlTable[9] used

#define dma_buffer_len 127
char dma_tx1[dma_buffer_len + 1], dma_tx2[dma_buffer_len + 1];
volatile int dma_now_write_at = 0, dma_now_count = 0; //, dma_send_done = 1;

#define is_uart0_dma_enabled (HWREG(UDMA_ENASET) & 512)
#define start_uart0_dma HWREG(UDMA_ENASET) = 512
#define uart0_dma_send_at(adr, num)                                                                      \
    {                                                                                                    \
        dmaControlTable[UDMA_CHANNEL_UART0TX].pvSrcEndAddr = (adr) + num - 1; /*end pointer*/            \
        int ui32Control = dmaControlTable[UDMA_CHANNEL_UART0TX].ui32Control & ~(UDMA_CHCTL_XFERSIZE_M);  \
        ui32Control |= (((num - 1) << UDMA_CHCTL_XFERSIZE_S) & UDMA_CHCTL_XFERSIZE_M) | UDMA_MODE_BASIC; \
        dmaControlTable[UDMA_CHANNEL_UART0TX].ui32Control = ui32Control;                                 \
        start_uart0_dma;                                                                                 \
    }

void init_dma()
{
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);

    MAP_uDMAEnable();
    MAP_uDMAControlBaseSet(dmaControlTable);

    uDMAChannelAssign(UDMA_CH9_UART0TX);

    //    uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0TX,
    //                                UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
    //                                    UDMA_ATTR_HIGH_PRIORITY |
    //                                    UDMA_ATTR_REQMASK);
    uDMAChannelAttributeEnable(UDMA_CHANNEL_UART0TX, UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY);
    uDMAChannelControlSet(UDMA_PRI_SELECT | UDMA_CHANNEL_UART0TX, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
    uDMAChannelTransferSet(UDMA_PRI_SELECT | UDMA_CHANNEL_UART0TX, UDMA_MODE_BASIC, dma_tx1, (void *)(UART0_BASE + UART_O_DR), 20);
    // uDMAIntRegister(UDMA_INT_SW, int_dma_sw_cplt);
    // uDMAIntRegister(UDMA_INT_ERR, int_dma_err_cplt);
    // uDMAChannelEnable(UDMA_CHANNEL_UART0TX);
}

void UART0IntHandler(void);
void init_uart0()
{
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
                        (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                         UART_CONFIG_PAR_NONE));
    // UARTFIFODisable(UART0_BASE);
    UARTFIFOEnable(UART0_BASE);
    // when passing through 8 of 16 bytes, a interrupt is signaled
    UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);

    UARTDMAEnable(UART0_BASE, UART_DMA_TX);
    UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_DMATX);
    IntEnable(INT_UART0);

    // UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_TX);
    UARTIntRegister(UART0_BASE, UART0IntHandler);

    init_dma();
    init_adc_dma();
}

void UART0IntHandler(void)
{
    uint32_t flag = HWREG(UART0_BASE + UART_O_MIS); //UARTIntStatus(UART0_BASE, 1);
    if (flag)
        HWREG(UART0_BASE + UART_O_ICR) = flag;
    // UARTIntClear(UART0_BASE, flag);
    if (flag & UART_INT_RX)
    {
        static const int p = UART0_BASE + UART_O_FR;
        // while fifo not empty
        while (!(HWREG(p) & UART_FR_RXFE))
        {
            char c = HWREG(UART0_BASE);
            if (c != '\r')
                lily_in(c);
        }
        return;
    }
    if (!is_uart0_dma_enabled && dma_now_count)
    {
        // uart0_dma_send_at(dma_now_write_at ? dma_tx1 : dma_tx2, dma_now_count);
        dma_now_count--;
        dmaControlTable[9].pvSrcEndAddr = (dma_now_write_at ? dma_tx1 : dma_tx2) + dma_now_count;
        int ui32Control = dmaControlTable[9].ui32Control & ~(0x00003FF0);
        ui32Control |= ((((dma_now_count) << 4) & 0x00003FF0) | 0x00000001);
        dmaControlTable[9].ui32Control = ui32Control;
        (*((volatile uint32_t *)(0x400FF028))) = 512;

        dma_now_count = 0;                    //clear count
        dma_now_write_at = !dma_now_write_at; //change to another buffer to write
    }
}

void sends(const char *msg)
{
    const char new_line[] = "\r\n";
    if (*msg == '\0')
        return;
    char *p = dma_now_write_at ? dma_tx1 : dma_tx2;
    for (; *msg != '\0' && dma_now_count < dma_buffer_len; msg++)
    {
        if (*msg == '\n')
        {
            p[dma_now_count++] = *new_line;
            p[dma_now_count++] = new_line[1];
            continue;
        }
        if (*msg == '\a')
        {
            // beep(0.3f);
            continue;
        }
        p[dma_now_count++] = *msg;
    }

    // if (!uDMAChannelIsEnabled(UDMA_CHANNEL_UART0TX))
    if (!is_uart0_dma_enabled) //1 << UDMA_CHANNEL_UART0TX)
    {
        // uart0_dma_send_at(p, dma_now_count);
        dma_now_count--;
        dmaControlTable[9].pvSrcEndAddr = (p) + dma_now_count;
        int ui32Control = dmaControlTable[9].ui32Control & ~(0x00003FF0);
        ui32Control |= ((((dma_now_count) << 4) & 0x00003FF0) | 0x00000001);
        dmaControlTable[9].ui32Control = ui32Control;
        (*((volatile uint32_t *)(0x400FF028))) = 512;

        dma_now_count = 0;
        dma_now_write_at = !dma_now_write_at;
    }
    // buffer overflow, send the following data
    if (*msg != '\0')
    {
        // while (uDMAChannelIsEnabled(UDMA_CHANNEL_UART0TX))
        while (is_uart0_dma_enabled)
        {
            // osDelay(1);
        }
        sends(msg);
        // HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), 100);
    }
}

// 10ms task to read data left in RXFIFO
int task_rx_fifo_monitor()
{
    static const int p = UART0_BASE + UART_O_FR;
    // while fifo not empty
    while (!(HWREG(p) & UART_FR_RXFE))
    {
        // char c = UARTCharGet(UART0_BASE);
        char c = HWREG(UART0_BASE);
        if (c != '\r')
            lily_in(c);
    }
    return 0;
}
